<?php
// 公共

namespace JyWxThird\BasicService;

use JyWxThird\Sdk\WXBizMsgCrypt;

class Common
{
  protected $domainUrl = 'https://api.weixin.qq.com';
  protected $config = [];
  protected $error = null;
  protected $access_token = null;            // 公众号 或 小程序的授权token
  protected $authorizer_access_token = null; // 公众号 或 小程序的授权给第三方平台的token(和$access_token一致，区分开只是不易搞混乱)
  
  protected $errorMsg = [
    '-40001' => '签名验证错误',
    '-40002' => 'xml解析失败',
    '-40003' => 'sha加密生成签名失败',
    '-40004' => 'encodingAesKey 非法',
    '-40005' => 'appid 校验错误',
    '-40006' => 'aes 加密失败',
    '-40007' => 'aes 解密失败',
    '-40008' => '解密后得到的buffer非法',
    '-40009' => 'base64加密失败',
    '-40010' => 'base64解密失败',
    '-40011' => '生成xml失败',
    '20001'  => '系统错误（包含该账号下无该模板等）',
    '20002'  => '参数错误',
    '40001'  => '获取 access_token 时 AppSecret 错误，或者 access_token 无效。请认真比对 AppSecret 的正确性，或查看是否正在为恰当的公众号调用接口',
    '40002'  => '暂无生成权限',
    '40003'  => 'touser字段openid为空或者不正确',
    '40007'  => '不合法的媒体文件 id',
    '40013'  => '生成权限被封禁',
    '40029'  => '不合法的code（code不存在、已过期或者使用过）',
    '40037'  => '订阅模板id为空不正确，请使用 选用模板 返回的 priTmplId',
    '40097'  => 'env_version 不合法/参数不合法',
    '40125'  => '无效的appsecret',
    '40163'  => 'oauth_code已被使用，请重新调用wx.login获取',
    '40166'  => '当前小程序appid未关联到服务号下，请先关联',
    '41004'  => '缺少 secret 参数',
    '41030'  => 'page路径不正确',
    '42001'  => 'access_token 已过期',
    '43101'  => '用户拒绝接受消息，如果用户之前曾经订阅过，则表示用户取消了订阅关系',
    '44003'  => '图文消息内容为空',
    '44990'  => '生成Scheme频率过快（超过100次/秒）',
    '45009'  => '接口调用超过限制',
    '47001'  => '解析 JSON/XML 内容错误',
    '47003'  => '模板参数不准确，可能为空或者不满足规则，errmsg会提示具体是哪个字段出错',
    '48001'  => 'api 功能未授权，请确认公众号/小程序已获得该接口，可以在公众平台官网 - 开发者中心页中查看接口权限',
    '53010'  => '名称格式不合法',
    '53011'  => '名称检测命中频率限制',
    '53012'  => '禁止使用该名称',
    '53013'  => '公众号：名称与已有公众号名称重复;小程序：该名称与已有小程序名称重复',
    '53014'  => '公众号：公众号已有{名称 A+}时，需与该帐号相同主体才可申请{名称 A};小程序：小程序已有{名称 A+}时，需与该帐号相同主体才可申请{名称 A}',
    '53015'  => '公众号：该名称与已有小程序名称重复，需与该小程序帐号相同主体才可申请;小程序：该名称与已有公众号名称重复，需与该公众号帐号相同主体才可申请',
    '53016'  => '公众号：该名称与已有多个小程序名称重复，暂不支持申请;小程序：该名称与已有多个公众号名称重复，暂不支持申请',
    '53017'  => '公众号：小程序已有{名称 A+}时，需与该帐号相同主体才可申请{名称 A};小程序：公众号已有{名称 A+}时，需与该帐号相同主体才可申请{名称 A}',
    '53018'  => '名称命中微信号',
    '53019'  => '名称在保护期内',
    '61006'  => 'component_verify_ticket无效',
    '61010'  => 'code 过期了',
    '61070'  => '法人姓名与微信号不一致',
    '85001'  => '微信号不存在或微信号设置为不可搜索',
    '85002'  => '小程序绑定的体验者数量达到上限',
    '85003'  => '微信号绑定的小程序体验者达到上限',
    '85004'  => '微信号已经绑定',
    '85015'  => '该账号不是小程序账号',
    '85017'  => '域名输入为空，或者没有新增域名，请确认小程序已经添加了域名或该域名是否没有在第三方平台添加',
    '85018'  => '域名没有在第三方平台设置',
    '85052'  => '小程序已发布，请勿重复发布',
    '85058'  => '没有在审核的版本',
    '85064'  => '找不到草稿',
    '85065'  => '模板库已满',
    '85066'  => '链接错误',
    '85068'  => '测试链接不是子链接',
    '85069'  => '校验文件失败',
    '85070'  => 'URL命中黑名单，无法添加',
    '85071'  => '已添加该链接，请勿重复添加',
    '85072'  => '该链接已被占用',
    '85073'  => '二维码规则已满',
    '85074'  => '小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则',
    '85075'  => '个人类型小程序无法设置二维码规则',
    '85079'  => '小程序未发布',
    '85301'  => '存在 “不符合域名规则的域名”导致无修改。',
    '85302'  => '存在 “ 缺少ICP备案的域名”导致无修改',
    '85400'  => '长期有效Scheme达到生成上限10万',
    '85401'  => '参数expire_time填写错误，时间间隔大于1分钟且小于1年',
    '86004'  => '无效微信号',
    '86069'  => 'owner_setting必填字段字段缺失',
    '86070'  => 'notice_method必填字段字段缺失',
    '86072'  => 'store_expire_timestamp参数无效。如果是编码格式不对，也会报这个错',
    '86073'  => 'ext_file_media_id参数无效',
    '86074'  => '现网隐私协议不存在',
    '86075'  => '现网隐私协议的ext_file_media_id禁止修改',
    '88000'  => '没有发布权限',
    '89015'  => '已经关联该小程序',
    '89018'  => '解除消息已发送，请公众号管理员在微信[公众平台安全助手]确认',
    '89019'  => '业务域名无更改，无需重复设置',
    '89020'  => '尚未设置小程序业务域名，请先在第三方平台中设置小程序业务域名后在调用本接口',
    '89021'  => '请求保存的域名不是第三方平台中已设置的小程序业务域名或子域名',
    '89029'  => '业务域名数量超过限制，最多可以添加100个业务域名',
    '89231'  => '个人小程序不支持调用 setwebviewdomain 接口',
    '89248'  => '企业代码类型无效，请选择正确类型填写',
    '89249'  => '该主体已有任务执行中，距上次任务 24h 后再试',
    '89250'  => '未找到该任务',
    '89251'  => '模板消息已下发，待法人人脸核身校验',
    '89252'  => '法人&企业信息一致性校验中',
    '89253'  => '缺少参数',
    '89254'  => '第三方权限集不全，请补充权限集后重试',
    '89255'  => 'code参数无效，请检查code长度以及内容是否正确 ；注意code_type的值不同需要传的code长度不一样',
    '89401'  => '系统不稳定，请稍后再试，如多次失败请通过社区反馈',
    '89402'  => '该审核单不在待审核队列，请检查是否已提交审核或已审完',
    '89403'  => '本单属于平台不支持加急种类，请等待正常审核流程',
    '89404'  => '本单已加速成功，请勿重复提交',
    '89405'  => '本月加急额度不足，请提升提审质量以获取更多额度',
    '91001'  => '不是公众号快速创建的小程序，无法调用此接口',
    '91002'  => '小程序发布后不可改名',
    '91003'  => '改名状态不合法，小程序发布前可改名的次数为2次，请确认改名次数是否已达上限',
    '91004'  => '昵称不合法',
    '91005'  => '昵称 15 天主体保护',
    '91006'  => '昵称命中微信号',
    '91007'  => '昵称已被占用',
    '91008'  => '昵称命中 7 天侵权保护期',
    '91009'  => '需要提交材料',
    '91010'  => '其他错误',
    '91011'  => '查不到昵称修改审核单信息',
    '91014'  => '+号规则 同一类型关联名主体不一致',
    '91015'  => '指的是已经有同名的公众号，但是那个公众号的主体和当前小程序主体不一致',
    '91016'  => '名称占用者 ≥2',
    '91017'  => '+号规则 不同类型关联名主体不一致',
    '200011' => '此账号已被封禁，无法操作',
    '200012' => '私有模板数已达上限，上限 50 个',
    '200013' => '此模版已被封禁，无法选用',
    '200014' => '模版 tid 参数错误，模板可能不存在',
    '200020' => '关键词列表 kidList 参数错误',
    '200021' => '场景描述 sceneDesc 参数错误',
  ];
  
  public function __construct($config = null)
  {
    $this->config = $config;
  }
  
  /**
   * 小程序或公众号专属调用
   *
   * @param string $access_token
   */
  public function set_access_token($access_token)
  {
    $this->access_token = $access_token;
  }
  
  public function get_access_token()
  {
    return $this->access_token;
  }
  
  public function set_authorizer_access_token($authorizer_access_token)
  {
    $this->authorizer_access_token = $authorizer_access_token;
  }
  
  public function get_authorizer_access_token()
  {
    return $this->authorizer_access_token;
  }
  
  public function getError()
  {
    return $this->error;
  }
  
  /**
   * 覆盖错误描述
   *
   * @param string $key
   * @param string $value
   */
  protected function coverError($key, $value)
  {
    $this->errorMsg[$key] = $value;
  }
  
  protected function setError($error)
  {
    if (isset($error['errcode']) && isset($this->errorMsg[$error['errcode']])) {
      $error['errmsg_cn'] = $this->errorMsg[$error['errcode']];
    } else if (!is_array($error) && isset($this->errorMsg[$error])) {
      $error = [
        'errcode'   => $error,
        'errmsg_cn' => $this->errorMsg[$error],
      ];
    }
    $this->error = $error;
    return false;
  }
  
  /**
   * 处理返回(适用于返回true 或 false)
   *
   * @param array  $res    返回数据
   * @param string $field1 当res包含指定字段时，返回整个res
   * @param string $field2 当res包含指定字段时，返回整个res
   */
  protected function handleReturn($res, $field1 = '', $field2 = '')
  {
    if ($field1 && isset($res[$field1]) && $res[$field1]) {
      return $res;
    } elseif ($field2 && isset($res[$field2]) && $res[$field2]) {
      return $res;
    } elseif (isset($res['errcode']) && $res['errcode'] == 0) {
      return true;
    } else {
      $this->setError($res);
      return false;
    }
  }
  
  /**
   * 解密消息
   *
   * @param $msgSignature
   * @param $timeStamp
   * @param $nonce
   * @param $from_xml
   * @return false|mixed
   */
  public function decryptMsg($msgSignature, $timeStamp, $nonce, $from_xml)
  {
    $pc      = new WXBizMsgCrypt($this->config['token'], $this->config['aeskey'], $this->config['appid']);
    $errCode = $pc->decryptMsg($msgSignature, $timeStamp, $nonce, $from_xml, $msg);
    if ($errCode == 0) {
      return $this->deXml($msg);
    } else {
      $this->setError($errCode);
      return false;
    }
  }
  
  /**
   * 解压xml
   *
   * @param string $xml 要解压的xml
   * @return mixed
   */
  protected function deXml($xml)
  {
    return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  }
}
